local ADDON_NAME = ...

local GearTable = CreateFrame("Frame", "GearTableFrame")
GearTable.items = {}
GearTable.rows = {}
GearTable.sortKey = "quality"
GearTable.sortAsc = false

local GetItemStatsCompat = C_Item and C_Item.GetItemStats or GetItemStats

local QUALITY_NAMES = {
    [0] = "Poor",
    [1] = "Common",
    [2] = "Uncommon",
    [3] = "Rare",
    [4] = "Epic",
    [5] = "Legendary",
}

local SLOT_LABELS = {
    INVTYPE_HEAD = "Head",
    INVTYPE_NECK = "Neck",
    INVTYPE_SHOULDER = "Shoulder",
    INVTYPE_CHEST = "Chest",
    INVTYPE_ROBE = "Chest",
    INVTYPE_WAIST = "Waist",
    INVTYPE_LEGS = "Legs",
    INVTYPE_FEET = "Feet",
    INVTYPE_WRIST = "Wrist",
    INVTYPE_HAND = "Hands",
    INVTYPE_FINGER = "Finger",
    INVTYPE_TRINKET = "Trinket",
    INVTYPE_CLOAK = "Back",
    INVTYPE_WEAPON = "One-Hand",
    INVTYPE_SHIELD = "Shield",
    INVTYPE_2HWEAPON = "Two-Hand",
    INVTYPE_WEAPONMAINHAND = "Main Hand",
    INVTYPE_WEAPONOFFHAND = "Off Hand",
    INVTYPE_RANGED = "Ranged",
    INVTYPE_RANGEDRIGHT = "Ranged",
    INVTYPE_HOLDABLE = "Held",
    INVTYPE_RELIC = "Relic",
    INVTYPE_THROWN = "Thrown",
    INVTYPE_TABARD = "Tabard",
}

local SLOT_ORDER = {
    Head = 1,
    Neck = 2,
    Shoulder = 3,
    Back = 4,
    Chest = 5,
    Wrist = 6,
    Hands = 7,
    Waist = 8,
    Legs = 9,
    Feet = 10,
    Finger = 11,
    Trinket = 12,
    ["Main Hand"] = 13,
    ["Off Hand"] = 14,
    ["One-Hand"] = 15,
    ["Two-Hand"] = 16,
    Shield = 17,
    Ranged = 18,
    Held = 19,
    Relic = 20,
    Thrown = 21,
    Tabard = 22,
}

local IGNORE_ITEMS = {
    ["Blacksmith Hammer"] = true,
    ["Mining Pick"] = true,
    ["Skinning Knife"] = true,
    ["Arclight Spanner"] = true,
}

local function SafeColorText(text, quality)
    local color = ITEM_QUALITY_COLORS[quality or 1]
    if not color then
        return text
    end

    local r = math.floor((color.r or 1) * 255 + 0.5)
    local g = math.floor((color.g or 1) * 255 + 0.5)
    local b = math.floor((color.b or 1) * 255 + 0.5)

    return string.format("|cff%02x%02x%02x%s|r", r, g, b, text)
end

local function GetQualityName(q)
    return QUALITY_NAMES[q or 1] or "Unknown"
end

local function GetSlotLabel(loc)
    return SLOT_LABELS[loc] or loc or "?"
end

local function GetContainerNumSlotsCompat(bag)
    if C_Container and C_Container.GetContainerNumSlots then
        return C_Container.GetContainerNumSlots(bag)
    end
    return GetContainerNumSlots(bag)
end

local function GetContainerItemLinkCompat(bag, slot)
    if C_Container and C_Container.GetContainerItemLink then
        return C_Container.GetContainerItemLink(bag, slot)
    end
    return GetContainerItemLink(bag, slot)
end

local function BuildStatSummary(link)
    local stats = GetItemStatsCompat and GetItemStatsCompat(link)
    if not stats then
        return ""
    end

    local parts = {}

    local function add(v, label)
        if v and v ~= 0 then
            table.insert(parts, string.format("%+d %s", v, label))
        end
    end

    add(stats["ITEM_MOD_STAMINA_SHORT"] or stats["ITEM_MOD_STAMINA"], "Stam")
    add(stats["ITEM_MOD_STRENGTH_SHORT"] or stats["ITEM_MOD_STRENGTH"], "Str")
    add(stats["ITEM_MOD_AGILITY_SHORT"] or stats["ITEM_MOD_AGILITY"], "Agi")
    add(stats["ITEM_MOD_DEFENSE_SKILL_RATING_SHORT"] or stats["ITEM_MOD_DEFENSE_SKILL_RATING"] or stats["DEFENSE"], "Def")
    add(stats["ITEM_MOD_HIT_RATING_SHORT"] or stats["ITEM_MOD_HIT_RATING"] or stats["ITEM_MOD_HIT_MELEE_RATING_SHORT"] or stats["ITEM_MOD_HIT_MELEE_RATING"], "Hit")
    add(stats["ITEM_MOD_CRIT_RATING_SHORT"] or stats["ITEM_MOD_CRIT_RATING"] or stats["ITEM_MOD_CRIT_MELEE_RATING_SHORT"] or stats["ITEM_MOD_CRIT_MELEE_RATING"], "Crit")
    add(stats["ITEM_MOD_DODGE_RATING_SHORT"] or stats["ITEM_MOD_DODGE_RATING"], "Dodge")
    add(stats["ITEM_MOD_PARRY_RATING_SHORT"] or stats["ITEM_MOD_PARRY_RATING"], "Parry")
    add(stats["ITEM_MOD_BLOCK_RATING_SHORT"] or stats["ITEM_MOD_BLOCK_RATING"], "Block")
    add(stats["ITEM_MOD_ATTACK_POWER_SHORT"] or stats["ITEM_MOD_ATTACK_POWER"], "AP")

    return table.concat(parts, ", ")
end

local function IsWantedItem(link)
    if not link then
        return false
    end

    local name, _, _, _, _, itemType, itemSubType, _, equipLoc = GetItemInfo(link)
    if not name then
        return false
    end

    if IGNORE_ITEMS[name] then
        return false
    end

    if not equipLoc or equipLoc == "" then
        return false
    end

    if itemType ~= ARMOR and itemType ~= WEAPON then
        return false
    end

    if itemType == WEAPON then
        if itemSubType == "Miscellaneous" then
            return false
        end

        local stats = GetItemStatsCompat and GetItemStatsCompat(link)
        if not stats or next(stats) == nil then
            return false
        end
    end

    return true
end

local function CompareItems(a, b)
    local key = GearTable.sortKey
    local asc = GearTable.sortAsc

    local function cmp(x, y)
        if asc then
            return x < y
        else
            return x > y
        end
    end

    if key == "quality" then
        if a.quality ~= b.quality then
            return cmp(a.quality or 0, b.quality or 0)
        end
        if a.slotOrder ~= b.slotOrder then
            return (a.slotOrder or 999) < (b.slotOrder or 999)
        end
        return (a.name or "") < (b.name or "")
    elseif key == "slot" then
        if a.slotOrder ~= b.slotOrder then
            return cmp(a.slotOrder or 999, b.slotOrder or 999)
        end
        if a.quality ~= b.quality then
            return (a.quality or 0) > (b.quality or 0)
        end
        return (a.name or "") < (b.name or "")
    elseif key == "name" then
        if a.name ~= b.name then
            return cmp(a.name or "", b.name or "")
        end
        if a.quality ~= b.quality then
            return (a.quality or 0) > (b.quality or 0)
        end
        return (a.slotOrder or 999) < (b.slotOrder or 999)
    elseif key == "stats" then
        if a.stats ~= b.stats then
            return cmp(a.stats or "", b.stats or "")
        end
        if a.quality ~= b.quality then
            return (a.quality or 0) > (b.quality or 0)
        end
        return (a.name or "") < (b.name or "")
    end

    return (a.name or "") < (b.name or "")
end

local function CreateHeaderButton(parent, text, width, x, sortKey)
    local btn = CreateFrame("Button", nil, parent)
    btn:SetSize(width, 20)
    btn:SetPoint("TOPLEFT", x, -42)

    local fs = btn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    fs:SetAllPoints()
    fs:SetJustifyH("LEFT")
    fs:SetText(text)
    btn.text = fs

    btn:SetScript("OnClick", function()
        if GearTable.sortKey == sortKey then
            GearTable.sortAsc = not GearTable.sortAsc
        else
            GearTable.sortKey = sortKey
            if sortKey == "quality" then
                GearTable.sortAsc = false
            else
                GearTable.sortAsc = true
            end
        end
        GearTable:Render()
    end)

    return btn
end

local function CreateWindow()
    local f = CreateFrame("Frame", "GearTableMain", UIParent, "BackdropTemplate")
    f:SetSize(980, 520)
    f:SetPoint("CENTER")
    f:SetMovable(true)
    f:EnableMouse(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", f.StartMoving)
    f:SetScript("OnDragStop", f.StopMovingOrSizing)
    f:SetClampedToScreen(true)
    f:SetBackdrop({
        bgFile = "Interface/Tooltips/UI-Tooltip-Background",
        edgeFile = "Interface/DialogFrame/UI-DialogBox-Border",
        tile = true,
        tileSize = 16,
        edgeSize = 24,
        insets = { left = 6, right = 6, top = 6, bottom = 6 },
    })
    f:SetBackdropColor(0, 0, 0, 0.9)

    tinsert(UISpecialFrames, f:GetName())

    local title = f:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("TOPLEFT", 14, -12)
    title:SetText("GearTable")

    local close = CreateFrame("Button", nil, f, "UIPanelCloseButton")
    close:SetPoint("TOPRIGHT", -5, -5)

    local headerBg = f:CreateTexture(nil, "ARTWORK")
    headerBg:SetColorTexture(0.15, 0.15, 0.15, 0.85)
    headerBg:SetPoint("TOPLEFT", 12, -40)
    headerBg:SetPoint("TOPRIGHT", -12, -40)
    headerBg:SetHeight(22)

    f.headerName = CreateHeaderButton(f, "Name", 330, 18, "name")
    f.headerQuality = CreateHeaderButton(f, "Quality", 95, 360, "quality")
    f.headerSlot = CreateHeaderButton(f, "Slot", 110, 465, "slot")
    f.headerStats = CreateHeaderButton(f, "Stats", 360, 585, "stats")

    local scroll = CreateFrame("ScrollFrame", nil, f, "UIPanelScrollFrameTemplate")
    scroll:SetPoint("TOPLEFT", 14, -66)
    scroll:SetPoint("BOTTOMRIGHT", -30, 14)

    local content = CreateFrame("Frame", nil, scroll)
    content:SetSize(1, 1)
    scroll:SetScrollChild(content)

    GearTable.window = f
    GearTable.content = content
end

function GearTable:Scan()
    wipe(self.items)

    for bag = 0, 4 do
        local numSlots = GetContainerNumSlotsCompat(bag) or 0
        for slot = 1, numSlots do
            local link = GetContainerItemLinkCompat(bag, slot)
            if link and IsWantedItem(link) then
                local name, _, quality, _, _, _, _, _, equipLoc = GetItemInfo(link)
                if name and equipLoc and equipLoc ~= "" then
                    local slotLabel = GetSlotLabel(equipLoc)

                    table.insert(self.items, {
                        name = name,
                        quality = quality or 1,
                        qualityName = GetQualityName(quality),
                        slot = slotLabel,
                        slotOrder = SLOT_ORDER[slotLabel] or 999,
                        stats = BuildStatSummary(link),
                        link = link,
                    })
                end
            end
        end
    end
end

function GearTable:ClearRows()
    for _, row in ipairs(self.rows) do
        row:Hide()
    end
end

function GearTable:CreateRow(index)
    local row = CreateFrame("Button", nil, self.content)
    row:SetSize(920, 20)

    if index == 1 then
        row:SetPoint("TOPLEFT", 0, 0)
    else
        row:SetPoint("TOPLEFT", self.rows[index - 1], "BOTTOMLEFT", 0, -2)
    end

    local bg = row:CreateTexture(nil, "BACKGROUND")
    bg:SetAllPoints()
    if index % 2 == 0 then
        bg:SetColorTexture(0.08, 0.08, 0.08, 0.65)
    else
        bg:SetColorTexture(0.12, 0.12, 0.12, 0.45)
    end
    row.bg = bg

    row.nameText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    row.nameText:SetPoint("LEFT", 6, 0)
    row.nameText:SetWidth(330)
    row.nameText:SetJustifyH("LEFT")

    row.qualityText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    row.qualityText:SetPoint("LEFT", 348, 0)
    row.qualityText:SetWidth(95)
    row.qualityText:SetJustifyH("LEFT")

    row.slotText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    row.slotText:SetPoint("LEFT", 455, 0)
    row.slotText:SetWidth(110)
    row.slotText:SetJustifyH("LEFT")

    row.statsText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    row.statsText:SetPoint("LEFT", 575, 0)
    row.statsText:SetWidth(330)
    row.statsText:SetJustifyH("LEFT")
    row.statsText:SetMaxLines(1)

    row:SetScript("OnEnter", function(self)
        if self.itemLink then
            GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
            GameTooltip:SetHyperlink(self.itemLink)
            GameTooltip:Show()
        end
    end)

    row:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)

    self.rows[index] = row
    return row
end

function GearTable:Render()
    if not self.window then
        return
    end

    table.sort(self.items, CompareItems)
    self:ClearRows()

    local count = #self.items
    local rowHeight = 22

    if count == 0 then
        local row = self.rows[1] or self:CreateRow(1)
        row.nameText:SetText("|cffff8080No armor or weapons found in your bags.|r")
        row.qualityText:SetText("")
        row.slotText:SetText("")
        row.statsText:SetText("")
        row.itemLink = nil
        row:Show()
        self.content:SetHeight(rowHeight)
        return
    end

    for i, item in ipairs(self.items) do
        local row = self.rows[i] or self:CreateRow(i)
        row.nameText:SetText(SafeColorText(item.name or "?", item.quality))
        row.qualityText:SetText(item.qualityName or "")
        row.slotText:SetText(item.slot or "?")
        row.statsText:SetText(item.stats ~= "" and item.stats or "-")
        row.itemLink = item.link
        row:Show()
    end

    self.content:SetHeight(count * rowHeight)
end

function GearTable:Toggle()
    if not self.window then
        CreateWindow()
    end

    if self.window:IsShown() then
        self.window:Hide()
    else
        self:Scan()
        self:Render()
        self.window:Show()
    end
end

SLASH_GEARTABLE1 = "/gt"
SLASH_GEARTABLE2 = "/geartable"
SlashCmdList["GEARTABLE"] = function()
    GearTable:Toggle()
end
